package cn.dongqihong.oauth2.web;

import cn.dongqihong.oauth2.common.ApiResult;
import cn.dongqihong.oauth2.common.ApiResultCode;
import cn.dongqihong.oauth2.common.SessionKey;
import cn.dongqihong.oauth2.entity.App;
import cn.dongqihong.oauth2.entity.AppToken;
import cn.dongqihong.oauth2.entity.User;
import cn.dongqihong.oauth2.service.AppService;
import cn.dongqihong.oauth2.service.UserService;
import com.alibaba.fastjson.JSONObject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import javax.servlet.http.HttpSession;
import java.util.Date;


@Controller
@RequestMapping(value = "oauth2")
public class OauthController {

    @Resource
    private AppService appService;
    @Resource
    private UserService userService;

    /**
     * 请求用户授权<br>
     * 授权成功后会重定向到回调地址上<br>
     * 如果用户未登录，引导至登录界面要求登录，如果已登录引导到回调地址并附带code
     *
     * @param redirectUrl 授权成功返回的地址，使用URL编码
     * @param appId       申请应用时分配的AppKey。
     * @param scope       授权范围。
     * @param state       其它附属信息，用于防止CSRF攻击，如果有会原样返回。
     * @return
     */
    @RequestMapping(value = "authorize.do")
    public String authorize(@RequestParam("redirect_url") String redirectUrl, @RequestParam("app_id") Long appId,
                            String scope, String state, HttpSession session, Model model) {
        User user = (User) session.getAttribute(SessionKey.LOGIN_USER);
        //生成Authorization Code
        Long code = appService.createToken(appId, user.getId());
        String params = "?code=" + code;
        // 有state
        if (!StringUtils.isEmpty(state)) {
            params += "&state=" + state;
        }
        // 用户已登录
        if (user != null) {
            return "redirect:" + redirectUrl + params;
        }

        // 查询应用
        App app = appService.findById(appId);
        if (app == null) {
            model.addAttribute("msg", "未授权该应用，请检查app_id参数是否有误。");
            return "error";
        }
        if (StringUtils.isEmpty(redirectUrl)) {
            model.addAttribute("msg", "回调地址为空，请检查redirect_url参数是否有误");
            return "error";
        }
        model.addAttribute(app);
        model.addAttribute("redirectUrl", redirectUrl);
        model.addAttribute("appId", appId);
        model.addAttribute("state", state);
        model.addAttribute("scope", scope);
        return "login";
    }

    /**
     * 用户登录授权，获取code
     *
     * @param name        账号
     * @param password    密码
     * @param redirectUrl 成功后转到的页面
     * @param appId       应用ID
     * @param state       其它附属信息，用于防止CSRF攻击。
     * @param model
     * @return 重定向地址
     */
    @RequestMapping(value = "login.do")
    public String login(String name, String password, String redirectUrl, Long appId, String state, Model model) {
        User user = userService.findByName(name);
        if (user == null) {
            model.addAttribute("msg", "用户不存在");
            return "error";
        }
        if (!user.getPassword().equals(password)) {
            model.addAttribute("msg", "用户名或密码错误");
            return "error";
        }

        //创建应用token,并返回code
        Long code = appService.createToken(appId, user.getId());

        return "redirect:" + redirectUrl + "?code=" + code + "&state=" + state;
    }


    /**
     * 用户登录授权，返回代code的回调地址
     *
     * @param name        账号
     * @param password    密码
     * @param redirectUrl 成功后转到的页面
     * @param appId       应用ID
     * @param state       其它附属信息，用于防止CSRF攻击。
     * @return 重定向地址
     */
    @ResponseBody
    @RequestMapping(value = "login2.do")
    public String login2(String name, String password, String redirectUrl, Long appId, String state) {
        User user = userService.findByName(name);
        if (user == null) {
            return ApiResult.create(ApiResultCode.USER_LOGIN_ERROR).toJsonStr();
        }
        if (!user.getPassword().equals(password)) {
            return ApiResult.create(ApiResultCode.USER_LOGIN_ERROR).toJsonStr();
        }

        //创建应用token,并返回code
        Long code = appService.createToken(appId, user.getId());

        return ApiResult.success(redirectUrl + "?code=" + code + "&state=" + state);
    }

    /**
     * 获取授权过的Access Token
     *
     * @param appId     申请应用时分配的AppID。
     * @param appSecret 申请应用时分配的AppSecret。
     * @param code      调用authorize获得的code值。
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "access_token.do")
    public String accessToken(@RequestParam("app_id") Long appId, @RequestParam("app_secret") String appSecret, Long code) {
        JSONObject json = new JSONObject();
        App app = appService.findById(appId);

        // 应用不存在或密码不对
        if (app == null || !app.getSecret().equalsIgnoreCase(appSecret)) {
            return ApiResult.create(ApiResultCode.INVALID_CLIENT).toJsonStr();
        }

        AppToken appToken = appService.findTokenByCode(code);
        if (new Date().after(appToken.getValidTill())) {
            return ApiResult.create(ApiResultCode.EXPIRED_TOKEN).toJsonStr();
        }

        return makeReturn(appToken).toJSONString();
    }

    /**
     * 刷新授权过的Access Token
     *
     * @param app_id        应用ID
     * @param refresh_token 用户刷新access_token
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "refresh_token.do")
    public String refreshToken(Long app_id, String refresh_token) {
        App app = appService.findById(app_id);

        if (app == null) {
            return ApiResult.create(ApiResultCode.INVALID_CLIENT).toJsonStr() ;
        }
        AppToken appToken = appService.refreshToken(refresh_token);
        return makeReturn(appToken).toJSONString();
    }

    /**
     * 查询用户access_token的授权相关信息
     *
     * @param access_token 用户授权时生成的access_token。
     * @return 结果
     */
    @ResponseBody
    @RequestMapping(value = "verify_token.do")
    public String verifyToken(String access_token) {
        AppToken appToken = appService.findTokenByAccessToken(access_token);
        if (appToken == null) {
            return null;
        }
        return makeReturn(appToken).toJSONString();
    }

    /**
     * 通过AppToken生成返回json
     *
     * @param appToken
     * @return
     */
    private JSONObject makeReturn(AppToken appToken) {
        JSONObject json = new JSONObject();
        json.put("access_token", appToken.getAccessToken());
        json.put("refresh_token", appToken.getRefreshToken());
        json.put("valid_till", appToken.getValidTill());
        return json;
    }
}
